﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Xml.Linq;

namespace Skynet.CommonLibrary.Extension.XML
{
    /// <summary>
    /// 生成XML验证文件(*.XSD）
    /// </summary>
    ///<remarks>
    /// 作者：         李志强
    /// 开发时间：  2011-04-11
    /// </remarks>
   public  class XSDExtension
    {
       /// <summary>
       /// 通过实体生成XSD验证
       /// </summary>
       /// <typeparam name="T">实体类型</typeparam>
       /// <returns>xml字符串</returns>
       public  string  EntityToXSD<T>() where T : class
       {
           Type t = typeof(T);

           XNamespace xn = "http://www.w3.org/2001/XMLSchema";
           XDocument doc = new XDocument(
               new XDeclaration("1.0", "utf-8", "yes"),
               new XElement(xn + "schema",
                   new XAttribute("elementFormDefault", "qualified"),
                   new XAttribute(XNamespace.Xmlns + "xs", "http://www.w3.org/2001/XMLSchema"),
                   new XElement(xn + "element",
                       new XAttribute("name", "Table"),
                       new XAttribute("type", "Table"))
                   ));


           XElement tableElement = new XElement(xn + "complexType",
               new XAttribute("name", "Table"));

           tableElement.Add(
               new XElement(xn + "sequence",
                   new XElement(xn + "element",
               //最少出现次数 （可无）
                       new XAttribute("minOccurs", "0"),
               //最多出现次数 （可以出现多次）
                       new XAttribute("maxOccurs", "unbounded"),
                       new XAttribute("name", "Row"),
                       new XAttribute("type", t.Name)
                       )),
               new XElement(xn + "attribute",
                   new XAttribute("name", "CreateTime"),
                   new XAttribute("type", "xs:string")),
               new XElement(xn + "attribute",
                   new XAttribute("name", "Name"),
                   new XAttribute("type", "xs:string"))
           );

           if (doc.Root != null)
           {
               doc.Root.Add(tableElement);
               AddEmptySimptyType(xn,doc.Root);

               CreateComplexType(t, doc.Root);
           }
           return  doc.ToString();
       }

        private  void CreateComplexType(Type t, XElement root)
        {
            XNamespace xn = root.GetNamespaceOfPrefix("xs");
            XElement complexTypeElement = new XElement(
                xn + "complexType",
                new XAttribute("name", t.Name));
            #region 循环所有属性

            foreach (var p in t.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance))//循环所有属性
            {
                //判断是否是Nullable<>类型
                Type pType = p.GetPropertyType();
            

                //这里仍然是分几种情况
                if (!GeneralType.Contains(pType.FullName))
                {
                    AddListType(root, p, xn, complexTypeElement);
                }
                else
                {
                    AddSimpleType(root, p,xn,complexTypeElement);
                }
            }
            #endregion
            root.Add(complexTypeElement);
        }

       //值类型验证
        public Func<PropertyInfo, ValueTypeLimitData> FuncValueTypeVerty
        { get; set; }
       //字符串类型验证
        public Func<PropertyInfo, ClassLimitData> FuncClassVerty
        { get; set; }


        private ValueTypeLimitData GetValueTypeVerty(PropertyInfo t)
        {
            if (FuncValueTypeVerty != null)
            {
                return FuncValueTypeVerty(t);
            }
            return default(ValueTypeLimitData);
            //object[] objs = t.GetCustomAttributes(typeof(ColumnMapAttribute), false);
            //if (objs.Count() == 0)
            //    return null;
            //ColumnMapAttribute column = objs[0] as ColumnMapAttribute;
            //if (column.MaxLength != null && column.MaxLength != 0)
            //{
            //    return column.MaxLength;
            //}
            //return null;
        }
        private ClassLimitData GetClassVerty(PropertyInfo info)
        {
            if (FuncClassVerty != null)
            {
                return FuncClassVerty(info);
            }
            return default(ClassLimitData);
        }


       //简单类型处理
        private void AddSimpleType(XElement root, PropertyInfo info, XNamespace xn, XElement complexTypeElement)
        {
            if (!info.PropertyType.IsValueType)
            {
                bool isSimple = false;
                XElement restrictionElement = new XElement(xn + "restriction",
                                            new XAttribute("base", GeneralType.ConvertXSDType(info.PropertyType.FullName)));
                XElement simpleElement = new XElement(xn + "attribute",
                            new XAttribute("name", info.Name),
                                new XElement(xn + "simpleType",
                                  restrictionElement
                                    ));
                //正则验证
                ClassLimitData limitData = GetClassVerty(info);
                if (!string.IsNullOrEmpty(limitData.Regular))
                {
                    isSimple = true;
                    restrictionElement.Add(new XElement(xn + "pattern", new XAttribute("value", limitData.Regular)));
                }
                //不能为空字符串
                if (limitData.IsNull != null && limitData.IsNull.Value == false)
                {
                    isSimple = true;
                    restrictionElement.Add(new XElement(xn + "pattern", new XAttribute("value", @".*[^ ].*")));
                }
                //添加最大长度限制验证
                if (limitData.MaxLength != 0)
                {
                    isSimple = true;
                    restrictionElement.Add(new XElement(xn + "maxLength",
                                                new XAttribute("value", limitData.MaxLength)));
                }
                if (isSimple)
                {
                    root.Add(simpleElement);
                    XElement element = new XElement(xn + "attribute",
                             new XAttribute("ref", info.Name));
                    complexTypeElement.Add(element);
                }
                else
                {
                    //这种情况最简单，属性为标准内置类型，直接作为元素的Attribute即可
                    complexTypeElement.Add(new XElement(xn + "attribute",
                              new XAttribute("name", info.Name),
                              new XAttribute("type", GeneralType.ConvertXSDType(info.PropertyType.FullName))));
                }
            }
            //值类型
            else
            {
                ValueTypeLimitData limitData = GetValueTypeVerty(info);
                if (limitData.Max != 0)
                {
                    //<xs:minInclusive value="0"/>
                    //<xs:maxInclusive value="120"/>
                    XElement restrictionElement = new XElement(xn + "restriction",
                                                             new XAttribute("base", "xs:integer"));
                    XElement simpleElement = new XElement(xn + "attribute",
                                new XAttribute("name", info.Name),
                                    new XElement(xn + "simpleType",
                                      restrictionElement
                                        ));
                    restrictionElement.Add(new XElement(xn + "minInclusive",
                                                                new XAttribute("value", limitData.Min)));
                    restrictionElement.Add(new XElement(xn + "maxInclusive",
                                                            new XAttribute("value", limitData.Max)));
                    root.Add(simpleElement);
                    XElement element = new XElement(xn + "attribute",
                             new XAttribute("ref", info.Name));
                    complexTypeElement.Add(element);
                }
                //值可空类型
                else if (limitData.IsNull != null && limitData.IsNull == false)
                {
                    // <xs:simpleType name="decimal-or-empty">  
                    // <xs:union memberTypes="xs:decimal empty-string" />  
                    // </xs:simpleType>  
                    // <xs:simpleType name="empty-string">  
                    //  <xs:restriction base="xs:string">  
                    //    <xs:enumeration value="" />  
                    //  </xs:restriction>  
                    //</xs:simpleType>  
                    XElement simpleEmptyElement = new XElement(xn + "simpleType",
                                      new XAttribute("name", "decimal-or-empty"),
                                      new XElement(xn + "union",
                                          new XAttribute("memberTypes", "xs:decimal empty-string")));
                    XElement simpleValueElement = new XElement(xn + "simpleType",
                                           new XAttribute("name", "empty-string"),
                                           new XElement(xn + "restriction",
                                               new XAttribute("base", "xs:string"),
                                               new XElement(xn + "enumeration",
                                                   new XAttribute("value", ""))));
                    root.Add(simpleEmptyElement);
                    root.Add(simpleValueElement);
                    XElement element = new XElement(xn + "attribute",
                             new XAttribute("name", info.Name),
                             new XAttribute("type", "decimal-or-empty"),
                             new XAttribute("use", "required"));
                    complexTypeElement.Add(element);
                }
                else
                {
                    complexTypeElement.Add(new XElement(xn + "attribute",
                        new XAttribute("name", info.Name),
                        new XAttribute("type", GeneralType.ConvertXSDType(info.PropertyType.FullName))));
                }
            }
        }

       //复杂类型处理
        private void AddListType(XElement root, PropertyInfo info, XNamespace xn, XElement complexTypeElement)
        {
            Type pType = info.PropertyType;
            var seqelement = complexTypeElement.Element(xn + "sequence");
            if (seqelement == null)
            {
                seqelement = new XElement(xn + "sequence");
                complexTypeElement.AddFirst(seqelement);
            }

            if (pType.IsEnum)//如果是枚举
            {
                seqelement.Add(
                    new XElement(
                        xn + "element",
                        new XAttribute("minOccurs", "0"),
                        new XAttribute("maxOccurs", "1"),
                        new XAttribute("name", info.Name),
                        new XAttribute("type", pType.Name)));

                XElement enumElement = new XElement(
                    xn + "complexType",
                    new XAttribute("name", pType.Name),
                    new XElement(xn + "attribute",
                        new XAttribute("name", "Enum"),
                        new XAttribute("type", "xs:string")));
                root.Add(enumElement);

            }
            else if (pType.GetInterface(typeof(IList).FullName) != null && pType.IsGenericType)
            //如果是集合,并且是泛型集合
            {

                Type itemType = pType.GetGenericArguments()[0];
                seqelement.Add(
                    new XElement(
                        xn + "element",
                        new XAttribute("minOccurs", "0"),
                        new XAttribute("maxOccurs", "unbounded"),
                        new XAttribute("name", info.Name),
                        new XAttribute("type", "ArrayOf" + info.Name)));

                XElement arrayElement = new XElement(
                    xn + "complexType",
                    new XAttribute("name", "ArrayOf" + info.Name),
                    new XElement(xn + "sequence",
                        new XElement(xn + "element",
                            new XAttribute("minOccurs", "0"),
                            new XAttribute("maxOccurs", "unbounded"),
                            new XAttribute("name", itemType.Name),
                            new XAttribute("type", itemType.Name))));

                root.Add(arrayElement);

                CreateComplexType(itemType, root);

            }
            else if (pType.IsClass || pType.IsValueType)
            {

                XElement element = new XElement(
                        xn + "element",
                        new XAttribute("minOccurs", "0"),
                        new XAttribute("maxOccurs", "1"),
                        new XAttribute("name", info.Name),
                        new XAttribute("type", pType.Name));

                seqelement.Add(element);

                CreateComplexType(pType, root);
            }
        }

       //添加可空值类型
        private void AddEmptySimptyType(XNamespace xn, XElement root)
        {
            // <xs:simpleType name="decimal-or-empty">  
            // <xs:union memberTypes="xs:decimal empty-string" />  
            // </xs:simpleType>  
            // <xs:simpleType name="empty-string">  
            //  <xs:restriction base="xs:string">  
            //    <xs:enumeration value="" />  
            //  </xs:restriction>  
            //</xs:simpleType>  
            //值类型
            XElement simpleEmptyElement = new XElement(xn + "simpleType",
                              new XAttribute("name", "decimal-or-empty"),
                              new XElement(xn + "union",
                                  new XAttribute("memberTypes", "xs:decimal empty-string")));
            XElement simpleValueElement = new XElement(xn + "simpleType",
                                   new XAttribute("name", "empty-string"),
                                   new XElement(xn + "restriction",
                                       new XAttribute("base", "xs:string"),
                                       new XElement(xn + "enumeration",
                                           new XAttribute("value", ""))));
            root.Add(simpleEmptyElement);
            root.Add(simpleValueElement);
            //时间类型
            simpleEmptyElement = new XElement(xn + "simpleType",
                        new XAttribute("name", "datetime-or-empty"),
                        new XElement(xn + "union",
                            new XAttribute("memberTypes", "xs:dateTime datetimeempty-string")));
            simpleValueElement = new XElement(xn + "simpleType",
                                   new XAttribute("name", "datetimeempty-string"),
                                   new XElement(xn + "restriction",
                                       new XAttribute("base", "xs:string"),
                                       new XElement(xn + "enumeration",
                                           new XAttribute("value", ""))));
            root.Add(simpleEmptyElement);
            root.Add(simpleValueElement);
        }

        public struct ClassLimitData
        {
            public int MaxLength { get; set; }
            public string Regular { get; set; }
            public bool? IsNull { get; set; }
        }
        public struct ValueTypeLimitData
        {
            public int Min { get; set; }
            public int Max { get; set; }
            public bool? IsNull { get; set; }
        }
        private  class GeneralType
        {
            private static readonly List<string> GeneralTypes = new List<string>
                                                                    {
            "System.Byte",//typeof(byte).FullName,
            "System.SByte",//typeof(sbyte).FullName,
            "System.Int16",//typeof(short).FullName,
            "System.UInt16",//typeof(ushort).FullName,
            "System.Int32",//typeof(int).FullName,
            "System.UInt32",//typeof(uint).FullName,
            "System.Int64",//typeof(long).FullName,
            "System.UInt64",//typeof(ulong).FullName,
            "System.Double",//typeof(double).FullName,
            "System.Decimal",//typeof(decimal).FullName,
            "System.Single",//typeof(float).FullName,
            "System.Char",//typeof(char).FullName,
            "System.Boolean",//typeof(bool).FullName,
            "System.String",//typeof(string).FullName,
            "System.DateTime"//typeof(DateTime).FullName
        };


            /// <summary>
            /// 判断当前给定类型是否为默认的数据类型
            /// </summary>
            /// <param name="fullType"></param>
            /// <returns></returns>
            internal static bool Contains(string fullType)
            {
                return GeneralTypes.Contains(fullType);
            }


            internal static string ConvertXSDType(string fullType)
            {
                switch (fullType)
                {
                    case "System.String":
                        return "xs:string";
                    case "System.Int32":
                        return "xs:int";
                    case "System.DateTime":
                        return "xs:dateTime";
                    case "System.Boolean":
                        return "xs:boolean";
                    case "System.Single":
                        return "xs:float";
                    case "System.Byte":
                        return "xs:byte";
                    case "System.SByte":
                        return "xs:unsignedByte";
                    case "System.Int16":
                        return "xs:short";
                    case "System.UInt16":
                        return "xs:unsignedShort";
                    case "System.UInt32":
                        return "xs:unsignedInt";
                    case "System.Int64":
                        return "xs:long";
                    case "System.UInt64":
                        return "xs:unsignedLong";
                    case "System.Double":
                        return "xs:double";
                    case "System.Decimal":
                        return "xs:decimal";
                }

                return string.Empty;
            }
        }
    }
}
